#ifndef __MORFINGABS_H__
#define __MORFINGABS_H__

#include "AlgeBSpline.h"

#define	MAX_MORF_SERIAL	10

template <class T, int DIMN>
class MorfABS: public AlgeBSpline<T, DIMN>
{
public:
	//MorfABS(void);
	~MorfABS(void);

	MorfABS(AlgeBSpline<T, DIMN>* ori, AlgeBSpline<T, DIMN>* dest);
	void InsertMorfSurf(AlgeBSpline<T, DIMN>* dest);
	void CreateData0(AlgeBSpline<T, DIMN>* ori);
	void CompactMorf();
	void SetIncreament(T increa[MAX_MORF_SERIAL-1]);
	T* GetMorfData();

public:
	AlgeBSpline<T,DIMN>*	m_pABS;
	int						m_nAllDataSize;
	int						m_nDataSize;
	int						m_nMorfSerialN;
	int						m_nMorfCurrent;
	bool*					m_SurfFlag[MAX_MORF_SERIAL];
	T						m_morfDelta;
	T*						m_sourData[MAX_MORF_SERIAL];
	//T*					m_destData;
	T*						m_morfData;
	T						m_increament[MAX_MORF_SERIAL-1];
};

//template  <class T, int DIMN>
//MorfABS<T,DIMN>::MorfABS(void)
//{
//	m_pABS = NULL;
//	m_morfDelta = 0;
//	m_destData = NULL;
//	m_morfData = NULL;
//	m_increament = 0.05;
//}

template  <class T, int DIMN>
MorfABS<T,DIMN>::~MorfABS(void)
{
	if(m_pABS)	delete m_pABS;
	m_pABS = NULL;

	m_morfDelta = 0;
	for (int i=0; i<m_nMorfSerialN; i++)
	{
		DELETEARR(m_sourData[i]);
		DELETEARR(m_SurfFlag[i]);
	}
	if(m_morfData) DELETEARR(m_morfData);	
}
template  <class T, int DIMN>
T* MorfABS<T,DIMN>::GetMorfData()
{
	m_morfDelta += m_increament[m_nMorfCurrent];
	m_morfDelta = max(0, m_morfDelta);
	m_morfDelta = min(1.0, m_morfDelta);
	T t=1.0-m_morfDelta;	
	for (int i=0; i<m_nDataSize; i++)
		m_morfData[i] = m_sourData[m_nMorfCurrent][i]*t+m_sourData[m_nMorfCurrent+1][i]*m_morfDelta;

	if(m_morfDelta==1.0)
	{
		if(m_nMorfCurrent<m_nMorfSerialN-2)			
		{
			m_morfDelta = 0.0;
			m_increament[m_nMorfCurrent] *= -1;
			m_nMorfCurrent++;
		}
		else		m_increament[m_nMorfCurrent] *= -1;//return from the end of serial
	}
	else if(m_morfDelta==0)
	{
		if(m_nMorfCurrent>0)
		{
			m_morfDelta = 1.0;
			m_increament[m_nMorfCurrent] *= -1;
			m_nMorfCurrent--;
		}
		else		m_increament[m_nMorfCurrent] *= -1;//Return to the head.

	}
	return m_morfData;
}

template  <class T, int DIMN>
MorfABS<T,DIMN>::MorfABS(AlgeBSpline<T, DIMN>* ori, AlgeBSpline<T, DIMN>* dest)
{
	CreateData0(ori);
	InsertMorfSurf(dest);
}

template  <class T, int DIMN>
void MorfABS<T,DIMN>::CreateData0(AlgeBSpline<T, DIMN>* ori)
{
	m_morfDelta = 0;
	m_nMorfCurrent = 0;
	m_nMorfSerialN = 1;
	//m_increament[m_nMorfCurrent] = 0.002;		//Change rate
	m_morfData = NULL;
	m_pABS = new AlgeBSpline<T, DIMN>(*ori);
	if(!m_pABS->m_ABez)	m_pABS->ABS2ABez();
	m_nAllDataSize = m_pABS->m_ABez->weight->GetDataSize();

	m_sourData[0] = new T[m_nAllDataSize];
	memcpy_s(m_sourData[0], m_nAllDataSize*sizeof(T), m_pABS->m_ABez->weight->GetData(), m_nAllDataSize*sizeof(T));
	m_SurfFlag[0] = new bool[m_pABS->m_segSize];
	memcpy_s(m_SurfFlag[0], m_pABS->m_segSize*sizeof(bool), m_pABS->m_ABez->flag->GetData(), m_pABS->m_segSize*sizeof(bool));

}

template  <class T, int DIMN>
void MorfABS<T,DIMN>::CompactMorf()
{
	int nPatchAdd=0, surI=0, nPatch;
	for (int i=0; i<m_pABS->m_segSize; i++)
	{
		if(m_SurfFlag[0][i])	continue;
		for (surI=1; surI<m_nMorfSerialN; surI++)
		{
			if(m_SurfFlag[surI][i])	break;
		}
		if(surI<m_nMorfSerialN)
		{
			m_SurfFlag[0][i] = true;
			nPatchAdd++;
		}
	}
	nPatch = m_pABS->GetActualPatchNum()+nPatchAdd;
	memcpy_s(m_pABS->m_ABez->flag->GetData(), m_pABS->m_segSize*sizeof(bool), m_SurfFlag[0], m_pABS->m_segSize*sizeof(bool));
	m_pABS->CompactData();	
	m_nDataSize = nPatch*m_pABS->m_degSize;


	printf("Morphing: ");
	printf("%d", m_pABS->m_ABez->seg[0]);
	for (int i=1; i<DIMN; i++)
		printf(" * %d", m_pABS->m_ABez->seg[1]);
	printf("= %d bezier domains with %d patches!\n", m_pABS->m_segSize, nPatch);


	int sizeAllData = m_nAllDataSize*sizeof(T);
	int compactSize = m_nDataSize*sizeof(T);
	for (surI=0; surI<m_nMorfSerialN; surI++)
	{
		memcpy_s(m_pABS->m_ABez->weight->GetData(), sizeAllData, m_sourData[surI], sizeAllData);
		m_pABS->CompactData();
		m_pABS->m_bReduced=false;
		memcpy_s(m_sourData[surI], compactSize, m_pABS->GetCompactFuncData(), compactSize);		
	}

	m_morfData = new T[m_nDataSize];
}
template  <class T, int DIMN>
void MorfABS<T,DIMN>::SetIncreament(T increa[MAX_MORF_SERIAL-1])
{
	for (int i=0; i<m_nMorfSerialN-1; i++)
	{
		m_increament[i] = increa[i];
	}
}
template  <class T, int DIMN>
void MorfABS<T,DIMN>::InsertMorfSurf(AlgeBSpline<T, DIMN>* dest)
{
	if(!dest->m_ABez) dest->ABS2ABez();
	if(m_nAllDataSize!=dest->m_ABez->weight->GetDataSize() || m_pABS->m_degSize!=dest->m_degSize || m_pABS->m_segSize!=dest->m_segSize)
	{
		printf("Error with morphing!\n");
		system("pause");
		exit(0);
	}

	m_sourData[m_nMorfSerialN] = new T[m_nAllDataSize];
	memcpy_s(m_sourData[m_nMorfSerialN], m_nAllDataSize*sizeof(T), dest->m_ABez->weight->GetData(), m_nAllDataSize*sizeof(T));
	m_SurfFlag[m_nMorfSerialN] = new bool[m_pABS->m_segSize];
	memcpy_s(m_SurfFlag[m_nMorfSerialN], dest->m_segSize*sizeof(bool), dest->m_ABez->flag->GetData(), dest->m_segSize*sizeof(bool));
	
	m_nMorfSerialN++;
}

typedef MorfABS<float, 3> MorfABSSf;
typedef MorfABS<float, 2> MorfABSCf;
typedef MorfABS<double, 3> MorfABSSd;
typedef MorfABS<double, 2> MorfABSCd;


#endif